﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TileMap
{
    class TileMapDownloader
    {
        private static readonly string SaveDirectory = "Texture";

        public static TileMapData DownLoad(double left,
            double right,
            double top,
            double bottom,
            int zoom,
            IMapTileFilter filter)
        {
            DirectoryInfo info = new DirectoryInfo(SaveDirectory);
            if (info.Exists)
            {
                info.Delete(true);
                while (info.Exists)
                {
                    Thread.Sleep(100);
                    info.Refresh();
                }
            }
            while (!info.Exists)
            {
                info.Create();
                Thread.Sleep(100);
                info.Refresh();
            }

            TileMapData res = new TileMapData();

            var bl = TencentMapTileCalc.WorldToTilePos(new Coordinate()
            {
                Lon = left,
                Lat = bottom
            }, zoom);

            var tr = TencentMapTileCalc.WorldToTilePos(new Coordinate()
            {
                Lon = right,
                Lat = top
            }, zoom);

            int delta = 2048 / 256;
            int columnCount = (int)Math.Ceiling((tr.X - bl.X) / (float)delta) + 1;
            int rowCount = (int)Math.Ceiling((tr.Y - bl.Y) / (float)delta) + 1;

            InitTileMapData(res, bl, columnCount, rowCount, delta, zoom);

            filter.ColumnCount = columnCount;
            filter.RowCount = rowCount;

            List<MapTileSave> saves = new List<MapTileSave>();
            for (int cIndex = 0; cIndex < columnCount; ++cIndex)
            {
                int c = bl.X + cIndex * delta;
                for (int rIndex = 0; rIndex < rowCount; ++rIndex)
                {
                    int r = bl.Y + rIndex * delta;
                    TileMapCellData cellData = CreateFromTile(c, r, c + delta, r + delta, zoom);
                    cellData.TextureFileName = SaveDirectory + "/" +
                        string.Format("Texture_{0}_{1}.jpg", rIndex, cIndex);
                    res.CellDatas[rIndex, cIndex] = cellData;

                    MapTileSave save = new MapTileSave()
                    {
                        Column = c,
                        ColumnCount = delta,
                        Row = r,
                        RowCount = delta,
                        SaveFileName = cellData.TextureFileName,
                        MinColumnInChildren = c,
                        MaxRowInChildren = r + delta - 1,
                        FilterColumn = cIndex,
                        FilterRow = rIndex
                    };
                    saves.Add(save);

                    for (int x = 0; x < delta; ++x)
                    {
                        for (int y = 0; y < delta; ++y)
                        {
                            save.AddCell(new MapCellDown()
                            {
                                Column = c + x,
                                Row = r + y,
                                Zoom = zoom
                            });
                        }
                    }
                }
            }
            Parallel.ForEach(saves,
                    new ParallelOptions()
                    {
                        MaxDegreeOfParallelism = 8
                    },
                    x => x.GetImageAsync(filter));

            return res;
        }

        private static void InitTileMapData(TileMapData res, Point bl, int columnCount, int rowCount, int delta, int zoom)
        {
            res.CellColumnCount = columnCount;
            res.CellRowCount = rowCount;
            res.CellDatas = new TileMapCellData[rowCount, columnCount];

            var blInWorld = TencentMapTileCalc.TileToWorldPos(bl, zoom);
            var trInWorld = TencentMapTileCalc.TileToWorldPos(
                new Point(bl.X + columnCount * delta, bl.Y + rowCount * delta),
                zoom);

            res.Left = blInWorld.Lon;
            res.Right = trInWorld.Lon;
            res.Bottom = blInWorld.Lat;
            res.Top = trInWorld.Lat;

            var blLonLat = MapPosCalc.LonLat2Mercator(blInWorld);
            var trLonLat = MapPosCalc.LonLat2Mercator(trInWorld);

            res.CellWidth = (trLonLat.Lon - blLonLat.Lon) / columnCount;
            res.CellHeight = (trLonLat.Lat - blLonLat.Lat) / rowCount;

        }

        private static TileMapCellData CreateFromTile(int c1, int r1, int c2, int r2, int zoom)
        {
            var bl = TencentMapTileCalc.TileToWorldPos(new Point(c1, r1), zoom);

            var tr = TencentMapTileCalc.TileToWorldPos(new Point(c2, r2), zoom);

            return new TileMapCellData()
            {
                Left = bl.Lon,
                Right = tr.Lon,
                Top = tr.Lat,
                Bottom = bl.Lat,
            };
        }
    }
}
